﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Text.RegularExpressions;
using System.IO;

namespace WindowsFormsApplication1
{
    public partial class frmTranscriptor : Form
    {
        #region DEFINITION: VARIABLES

        String aFile = "";
        const int SINGLETAG = 0;
        const int DOUBLETAG = 1;
        const int SPECIALTAG = 2;
        int counterOverlapping = 1;
        const int CURSOR_POS_OLD = 0;
        const int CURSOR_POS_NEW = 1;
        int cacheCaret = 0;
        const int PLAYER_WMP = 0;
        const int PLAYER_VLC = 1;
        const int OVERLAPPING_CONTINUE = 0;
        const int OVERLAPPING_NEXT = 1;
                
        #region DEFINITION: Tags

        const string TAG_BREAK = "break";
        const string TAG_CUT = "cut";
        const string TAG_UNCLEAR = "unclear";
        const string TAG_TRUNC = "trunc";
        const string TAG_ALTERNATIVE = "alternative";
        const string TAG_FOREIGN = "foreign";
        const string TAG_COMMENTS = "(";
        const string TAG_DOUBLEQUOTES = "\"";
        const string TAG_SINGLEQUOTES = "\'";
        const string TAG_SECTIONBOUNDARY = "#";
        const string TAG_SECTIONCOMMENT = "{";
        const string TAG_RETRO_MARKER = "retro";
        const string TAG_RETRO_RANGE = "TAG_RETRO_RANGE";
        const string TAG_OVERLAPPING_CONTINUED = "TAG_OVERLAPPING_CONTINUED";
        const string TAG_OVERLAPPING_NEW = "TAG_OVERLAPPING_NEW";
        const string TAG_TIMESTAMP = "TAG_TIMESTAMP";
        const int TAGBUTTON_SHOW = 1;
        const int TAGBUTTON_HIDE = 0;

        string[] tags = new string[] { "break", "cut", "unclear", "trunc", "alternative", 
            "foreign", "(", "\"", "\'", "#", "{", "retro", "TAG_OVERLAPPING_CONTINUED", "TAG_OVERLAPPING_NEW" };

        #endregion
                
        #region DEFINITION: Captions

        const string CAPTION_BREAK = "<break />";
        const string CAPTION_CUT = "<cut />";
        const string CAPTION_UNCLEAR_SINGLE = "<unclear />";
        const string CAPTION_UNCLEAR_DOUBLE = "<unclear>...</unclear>";
        const string CAPTION_TRUNC = "<trunc>...</trunc>";
        const string CAPTION_ALTERNATIVE = "<alternative>...</alt>";
        const string CAPTION_FOREIGN = "<foreign>...</foreign>";
        const string CAPTION_COMMENTS = "Comments ( )";
        const string CAPTION_QUOTES_DOUBLE = "Quotes \" \"";
        const string CAPTION_QUOTES_SINGLE = "Single Quotes ' ' (films etc.)";
        const string CAPTION_SECTIONBOUNDARY = "Section Boundary #";
        const string CAPTION_SECTIONCOMMENTS = "Section Comments { }";
        const string CAPTION_OVERLAPPING_CONTINUED = "Overlapping (contd.)";
        const string CAPTION_OVERLAPPING_NEW = "Overlapping (new)";

        string[] captions = new string[] { "<break />", "<cut />", "<unclear />", "<unclear>...</unclear>",
            "<trunc>...</trunc>", "<alternative>...</alt>", "<foreign>...</foreign>", "Comments ( )",
            "Quotes \" \"", "Single Quotes ' ' (films etc.)", "Section Boundary #", "Section Comments { }",
            "Overlapping (contd.)", "Overlapping (new)" };

        #endregion

        private void addContextMenuItem(string[] item)
        {
            for (int i = 0; i <= item.Count() - 1; i++)
            {
                mnuInsertTag.DropDownItems.Add(item[i], null, contextMenuTagItem_Click);
            }
        }

        private void createContextMenuItems()
        {
            addContextMenuItem(captions);

            /*
            addContextMenuItem([TAG_BREAK, TAG_CUT]);
            addContextMenuItem(TAG_CUT);
            addContextMenuItem(TAG_UNCLEAR);
            addContextMenuItem
            */
        }

        #endregion

        #region DEFINITION: TRANSCRIPTOR basics

        public frmTranscriptor()
        {
            InitializeComponent();

            //AXVLC.VLCPlugin2Class p = new AXVLC.VLCPlugin2Class();
            //p.addTarget("C:\\ef.avi", null,AXVLC.VLCPlaylistMode.VLCPlayListInsert,0);
            //p.play();

            int screenHeight = Screen.PrimaryScreen.WorkingArea.Height;
            int screenWidth = Screen.PrimaryScreen.WorkingArea.Width;

            this.Top = (screenHeight - this.Height) / 2;
            this.Left = (screenWidth - this.Width) / 2;
        }

        private void frmTranscriptor_Resize(object sender, EventArgs e)
        {
            sortVisibleItems();
        }
        
        private void frmTranscriptor_Load(object sender, EventArgs e)
        {
            // disable autostart
            wmPlayer.settings.autoStart = false;
            
            // fill Context Menu
            addContextMenuItem(captions);

            memText.AutoWordSelection = false;
        }

        #endregion

        #region DEFINITION: VISIBLE OBJECT - SIZE & POSITION

        private void splitContainer_SplitterMoved(object sender, SplitterEventArgs e)
        {
            //button1.Text = splitContainer.SplitterDistance.ToString();
            sortVisibleItems();
        }

        private void sortVisibleItems()
        {
            //grpbxVideo.Top = 8;
            //grpbxControls.Top = 8;
        }

        #endregion
        
        #region DEFINITION: HELPER FUNCTIONS
        
        private double extractTimestamp()
        {
            // this works with "," as separator
            return double.Parse(getTextBox().SelectedText);
        }

        public static bool isValidTimestamp(string strIn)
        {
            // Return true if strIn is in valid timestamp format
            return Regex.IsMatch(strIn, "^\\[((\\d+,\\d\\d)|0)\\]", RegexOptions.None);
        }

        private string getNextOverlappingCounter(int counterType)
        {
            if (counterType == OVERLAPPING_NEXT)
            {
                counterOverlapping = counterOverlapping + 1;
            }

            if (counterOverlapping.ToString().Length == 1)
            {
                return "00"+counterOverlapping.ToString();
            }
            else if (counterOverlapping.ToString().Length == 2)
            {
                return "0"+counterOverlapping.ToString();
            }
            else
            {
                return counterOverlapping.ToString();
            }
        }

        public static System.Text.Encoding GetType(string FILE_NAME)
            {
                FileStream fs = new FileStream(FILE_NAME, FileMode.Open, FileAccess.Read);
                Encoding r = GetType(fs);
                fs.Close();
                return r;
            }

        public static System.Text.Encoding GetType(FileStream fs)
        {
            byte[] Unicode = new byte[] { 0xFF, 0xFE, 0x41 };
            byte[] UnicodeBIG = new byte[] { 0xFE, 0xFF, 0x00 };
            byte[] UTF8 = new byte[] { 0xEF, 0xBB, 0xBF }; //with BOM
            Encoding reVal = Encoding.Default;

            BinaryReader r = new BinaryReader(fs, System.Text.Encoding.Default);
            int i;
            int.TryParse(fs.Length.ToString(), out i);
            byte[] ss = r.ReadBytes(i);
            if (IsUTF8Bytes(ss) || (ss[0] == 0xEF && ss[1] == 0xBB && ss[2] == 0xBF))
            {
                reVal = Encoding.UTF8;
            }
            else if (ss[0] == 0xFE && ss[1] == 0xFF && ss[2] == 0x00)
            {
                reVal = Encoding.BigEndianUnicode;
            }
            else if (ss[0] == 0xFF && ss[1] == 0xFE && ss[2] == 0x41)
            {
                reVal = Encoding.Unicode;
            }
            r.Close();
            return reVal;
        }

        private static bool IsUTF8Bytes(byte[] data)
        {
            int charByteCounter = 1;
            byte curByte;
            for (int i = 0; i < data.Length; i++)
            {
                curByte = data[i];
                if (charByteCounter == 1)
                {
                    if (curByte >= 0x80)
                    {
                        while (((curByte <<= 1) & 0x80) != 0)
                        {
                            charByteCounter++;
                        }

                        if (charByteCounter == 1 || charByteCounter > 6)
                        {
                            return false;
                        }
                    }
                }
                else
                {
                    if ((curByte & 0xC0) != 0x80)
                    {
                        return false;
                    }
                    charByteCounter--;
                }
            }
            if (charByteCounter > 1)
            {
                throw new Exception("Error byte format");
            }
            return true;
        }

        private void alert(string text)
        {
            MessageBox.Show(text);
        }

        private RichTextBox getTextBox()
        {
            return memText;
        }

        private void resetCounterOverlapping()
        {
            counterOverlapping = 0;
        }
        #endregion
        
        #region DEFINITION: BUTTONS

        #region DEFINITION: TAG BUTTONS

        private void btnRetro_Click(object sender, EventArgs e)
        {
            AppendTextToIndex(TAG_RETRO_MARKER, SPECIALTAG);
        }

        private void btnBreak_Click(object sender, EventArgs e)
        {
            AppendTextToIndex(TAG_BREAK, SINGLETAG);
            //insertTextAtCursor("<break />");
        }
        
        private void btnCut_Click(object sender, EventArgs e)
        {
            //insertTextAtCursor("<cut />");
            AppendTextToIndex(TAG_CUT, SINGLETAG);
        }

        private void btnUnclearSingle_Click(object sender, EventArgs e)
        {
            //insertTextAtCursor("<unclear />");
            AppendTextToIndex(TAG_UNCLEAR, SINGLETAG);
        }

        private void btnUnclearDouble_Click(object sender, EventArgs e)
        {
            AppendTextToIndex(TAG_UNCLEAR, DOUBLETAG);
        }

        private void btnTrunc_Click(object sender, EventArgs e)
        {
            AppendTextToIndex(TAG_TRUNC, DOUBLETAG);
        }

        private void btnAlternative_Click(object sender, EventArgs e)
        {
            AppendTextToIndex(TAG_ALTERNATIVE, DOUBLETAG);
        }

        private void btnForeign_Click(object sender, EventArgs e)
        {
            AppendTextToIndex(TAG_FOREIGN, DOUBLETAG);
        }

        private void btnComments_Click(object sender, EventArgs e)
        {
            AppendTextToIndex(TAG_COMMENTS, SPECIALTAG);
        }

        private void btnQuotesDouble_Click(object sender, EventArgs e)
        {
            AppendTextToIndex(TAG_DOUBLEQUOTES, SPECIALTAG);
        }

        private void btnQuotesSingle_Click(object sender, EventArgs e)
        {
            AppendTextToIndex(TAG_SINGLEQUOTES, SPECIALTAG);
        }

        private void btnSectionBoundary_Click(object sender, EventArgs e)
        {
            insertTextAtCursor(TAG_SECTIONBOUNDARY);
        }

        private void btnSectionComments_Click(object sender, EventArgs e)
        {
            AppendTextToIndex(TAG_SECTIONCOMMENT, SPECIALTAG);
        }

        #endregion

        private void btnTimestamp_Click(object sender, EventArgs e)
        {
            insertTime();
        }

        private void btnLoadVideo_Click(object sender, EventArgs e)
        {
            openFileDialog.FileName = "";
            openFileDialog.Filter = "All supported formats|*.wmv;*.aac;*.avi;*.m4a;*.mpg;*.3gp;*.mp4;*.mp3;*.wma";

            /*
            AXVLC.VLCPlugin vlcPlugin = new AXVLC.VLCPlugin();
            vlcPlugin.addTarget("C:\\Ezios_Family.avi", null, AXVLC.VLCPlaylistMode.VLCPlayListAppendAndGo, -666);
            vlcPlugin.play();
            */

            if (openFileDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK && openFileDialog.FileName.Length > 0)
            {
                //aFile = openFileDialog.FileName;
                //loadFileToPlayer(PLAYER_WMP, openFileDialog.FileName);
                loadFileToPlayer(PLAYER_WMP, openFileDialog.FileName);
                btnTimeStamp.Enabled = true;
            }

            getTextBox().Focus();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            Console.Out.WriteLine("hallo");
            
            //insertTime();
            //getTextBox().Focus();

            //Form1.ActiveForm.Activate();
            //Form1.ActiveForm.BringToFront();
            //Form1.ActiveForm.Focus();
            //Form1.ActiveForm.Show();
            
            //System.Windows.Forms.textpointer
        }

        private void btnOverlapping_Click(object sender, EventArgs e)
        {
            AppendTextToIndex(getNextOverlappingCounter(OVERLAPPING_CONTINUE), DOUBLETAG);
        }

        private void btnOverlappingNew_Click(object sender, EventArgs e)
        {
            AppendTextToIndex(getNextOverlappingCounter(OVERLAPPING_NEXT), DOUBLETAG);
        }
        #endregion

        #region DEFINITION: MENUS

        private void mnuClear_Click(object sender, EventArgs e)
        {
            DialogResult result;
            // if the user wants to close the transcript, he has to confirm this first
            result = MessageBox.Show("Closing file. Are you sure?", "Closing file", MessageBoxButtons.OKCancel, MessageBoxIcon.Question);
            if (result == DialogResult.OK)
            {
                // user is sure to clear the area
                getTextBox().Clear();
                resetCounterOverlapping();
            }
        }

        private void mnuClose_Click(object sender, EventArgs e)
        {
            Application.Exit();
        }

        private void mnuAbout_Click(object sender, EventArgs e)
        {
            MessageBox.Show("(c) Achim Skuta\nChair for Applied English Linguistics\nUniversity of Tübingen\n\nContact: Achim.Skuta@googlemail.com", "About ...", MessageBoxButtons.OK, MessageBoxIcon.Information);
        }

        private void mnuNew_Click(object sender, EventArgs e)
        {
            // strange stuff BACKBONE uses to tag it's transcripts
            getTextBox().Text = "[METADATA]\nTitle:\nDate Recording:\nDate Transcription:\nLocale:\nPrincipal Investigator: Kurt Kohn\nResearcher:\nTranscriber:\nEditor:\nInterview Description: \nAuthority: BACKBONE Project\nID:\nLanguage:\nMediaServerPath: http://purl.oclc.org/NET/backbone/media/ \nMediaFileName:\nLanguageVariery:\nParticipants:\n  person: \n  name: \n  role: \n  sex: \n  age: \n  description:\n[/METADATA]\n\n[BODY]\n\n[/BODY]";
            resetCounterOverlapping();
        }

        private void mnuSave_Click(object sender, EventArgs e)
        {
            if (aFile.Length == 0)
            { 
                // Set filter to save as text only
                saveFileDialog.Filter = "Text file|*.txt";
                if (saveFileDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK && saveFileDialog.FileName.Length > 0)
                {
                    aFile = saveFileDialog.FileName;
                }
            }
            
            // if aFile has been called before ...
            if (aFile.Length > 0)
            {
                // save textarea to file   
                getTextBox().SaveFile(aFile, RichTextBoxStreamType.PlainText);
                // and show a message box to confirm
                MessageBox.Show("Your file has been saved.");
            }
        }

        private void mnuOpen_Click(object sender, EventArgs e)
        {
            openFileDialog.FileName = "";
            openFileDialog.Filter = "Text file|*.txt";
            if (openFileDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK && openFileDialog.FileName.Length > 0)
            {
                
                aFile = openFileDialog.FileName;
                //alert("Type: "+GetType(aFile));
                loadTextFileToTextBox(aFile);

            }
            
        }

        private void mnuSaveAs_Click(object sender, EventArgs e)
        {
            // Set filter to save as text only
            saveFileDialog.Filter = "Text file|*.txt";
            if (saveFileDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK && saveFileDialog.FileName.Length > 0)
            {
                aFile = saveFileDialog.FileName;
            }
            
            if (aFile.Length > 0)
            {
                // save textarea to file   
                getTextBox().SaveFile(aFile, RichTextBoxStreamType.PlainText);
                // and show a message box to confirm
                MessageBox.Show("Your file has been saved.");
            }
        }

        #endregion

        #region DEFINITION: TEXTAREA MENU

        private void hereThereBeDragonsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (getTimestampFromCursor() > 0)
            {
                wmPlayer.Ctlcontrols.currentPosition = getTimestampFromCursor();
            }
        }

        #endregion

        #region DEFINITION: TEXTAREA FUNCTIONS

        private double getTimestampFromCursor()
        {
            string cache = "0";
            for (int i = 0; i < getTextBox().Lines[getLineFromCursorPos()].IndexOf("]"); i++)
            {
                string currentChar = getTextBox().Lines[getLineFromCursorPos()][i].ToString();
                if (!currentChar.Equals("[") && !currentChar.Equals("]")) 
                {
                    cache += currentChar;
                }
            }
            
            return double.Parse(cache);
        }

        private void memText_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button.ToString().Equals("Right"))
            {
                Point pos = this.PointToClient(Cursor.Position);
                contextMenu.Show(this, pos);
                //mnuInsertTag.DropDownItems.Add("Sag 'Hallo'", null, empty);
                //mnuInsertTag.DropDownItems.Add("Sag hallo", null, emptyToolStripMenuItem_Click);

            }
        }

        private void memText_KeyDown(object sender, KeyEventArgs e)
        {
            Keys VK_A = Keys.A;
            Keys VK_D = Keys.D;
            Keys VK_F = Keys.F;
            Keys VK_I = Keys.I;
            Keys VK_N = Keys.N;
            Keys VK_O = Keys.O;
            Keys VK_R = Keys.R;
            Keys VK_U = Keys.U;
            Keys VK_Y = Keys.Y;
            
            Keys VK_CTRL = Keys.Control;
            Keys VK_SPACE = Keys.Space;
            Keys VK_LEFT = Keys.Left;
            Keys VK_RIGHT = Keys.Right;
            Keys eKey = e.KeyCode;
            Keys modKey = Control.ModifierKeys;

            try
            {
                if (eKey == VK_F && modKey == VK_CTRL)
                {
                    //alert("Suche!");
                }
                else if (eKey == VK_SPACE && modKey == VK_CTRL)
                {
                    //alert("Play/Pause");
                    playerClick();
                }
                else if (eKey == VK_D && modKey == VK_CTRL)
                {
                    // warum springt das immer an den Anfang des Transcripts? :-(
                    //resetCursor(CURSOR_POS_OLD, 0);
                    insertTime();
                    //resetCursor(CURSOR_POS_NEW, 0);
                }
                else if ((eKey == VK_LEFT && modKey == VK_CTRL) || (eKey == VK_R && modKey == VK_CTRL))
                {
                    //alert("Move back"); -> Rewind
                    wmPlayer.Ctlcontrols.currentPosition = wmPlayer.Ctlcontrols.currentPosition - 5;
                }
                else if ((eKey == VK_RIGHT && modKey == VK_CTRL) || (eKey == VK_F && modKey == VK_CTRL))
                {
                    //alert("Move forward"); -> Forward
                    wmPlayer.Ctlcontrols.currentPosition = wmPlayer.Ctlcontrols.currentPosition + 5;
                }
                else if (eKey == VK_O && modKey == VK_CTRL)
                {
                    AppendTextToIndex(getNextOverlappingCounter(OVERLAPPING_CONTINUE), DOUBLETAG);
                }
                else if (eKey == VK_Y && modKey == VK_CTRL)
                {
                    AppendTextToIndex(TAG_ALTERNATIVE, DOUBLETAG);
                }
                else if (eKey == VK_N && modKey == VK_CTRL)
                {
                    AppendTextToIndex(getNextOverlappingCounter(OVERLAPPING_NEXT), DOUBLETAG);
                }
                else if (eKey == VK_U && modKey == VK_CTRL)
                {
                    AppendTextToIndex(TAG_UNCLEAR, DOUBLETAG);
                }
                else if (eKey == VK_I && modKey == VK_CTRL)
                {
                    AppendTextToIndex(TAG_UNCLEAR, SINGLETAG);
                }
            }
            catch (Exception ie)
            {
                alert("Error: " + ie.Message.ToString() + "\nCode: 100A");
                // deffo not working 
                // fehlerreproduktion:
                // Video laden, starten, anhalten, in den Textbereich gehen und Strg+D drücken
                // habe mal das resetCursor() ausgeklammert
            }
        }

        private void resetCursor(int pos, int stringLength)
        {
            if (pos == CURSOR_POS_OLD)
            {
                cacheCaret = getTextBox().SelectionStart;
            }
            else if (pos == CURSOR_POS_NEW)
            {
                // Move focus back to text area
                // re-setting the focus will move the text area to the line of the cursor, unfortunately :-/
                getTextBox().Focus();

                // reset cursor to old position

                getTextBox().SelectionStart = cacheCaret + stringLength; 
            }
        }
        
        private void insertTextAtCursor(string text)
        {
            resetCursor(CURSOR_POS_OLD, 0);
            getTextBox().Text = getTextBox().Text.Insert(getTextBox().SelectionStart, text);
            getTextBox().SelectionStart = getTextBox().SelectionStart + text.Length;

            resetCursor(CURSOR_POS_NEW, text.Length);
        }

        private void loadTextFileToTextBox(string file)
        {
            // got it from here: http://stackoverflow.com/questions/10041826/c-load-txt-to-richtextbox-and-convert-into-utf8
            try
            {
                string textFileContent = "";
                if (GetType(aFile).ToString() == "System.Text.UTF8Encoding")
                {
                    textFileContent = File.ReadAllText(aFile, Encoding.UTF8);                    
                }
                else if (GetType(aFile).ToString() == "System.Text.UnicodeEncoding")
                {
                    textFileContent = File.ReadAllText(aFile, Encoding.Unicode); 
                }
                else if (GetType(aFile).ToString() == "System.Text.SBCSCodePageEncoding")
                {
                    textFileContent = File.ReadAllText(aFile);
                }
                getTextBox().Text = textFileContent;
                resetCounterOverlapping();
            }
            catch (ArgumentException ae)
            {
                MessageBox.Show(ae.Message.ToString());
            }
        }   

        public void AppendTextToIndex(string tag, int type)
        {   // stolen from http://www.dreamincode.net/code/snippet2484.htm
            System.Windows.Forms.RichTextBox rtb = getTextBox();
            int selStart = rtb.SelectionStart;
            int selLen = rtb.SelectionLength;

            // pass the rich text box, the text to append, and the index to start appending
            // make sure the caret is still in the correct position after appending
            // we add text.Length so that it will still be in the same place in relation
            // to the words
            resetCursor(CURSOR_POS_OLD, 0);

            string cache;
            string tagStart = "";
            string tagEnd = "";

            try
            {

                switch (type)
                {
                    case SINGLETAG:
                        tagStart = "<" + tag + " />";
                        tagEnd = "";
                        break;
                    case DOUBLETAG:
                        tagStart = "<" + tag + ">";
                        tagEnd = "</" + tag + ">";
                        break;
                    case SPECIALTAG:

                        if (tag.Equals(TAG_COMMENTS))
                        {
                            // comments: ( )
                            tagStart = "(";
                            tagEnd = ")";
                        }
                        else if (tag.Equals(TAG_SECTIONCOMMENT))
                        {
                            // section comments: { }
                            tagStart = "{";
                            tagEnd = "}";
                        }
                        else if (tag.Equals(TAG_DOUBLEQUOTES) || tag.Equals(TAG_SINGLEQUOTES))
                        {
                            // Quotes: " and Single Quotes: '
                            tagStart = tag;
                            tagEnd = tagStart;
                        }
                        else if (tag.Equals(TAG_RETRO_MARKER))
                        {
                            tagStart = "\n<retro who=\"AB\" id=\"x\" />\n";
                            tagEnd = "";
                        }
                        else if (tag.Equals(TAG_OVERLAPPING_CONTINUED))
                        {
                            tagStart = "<" + getNextOverlappingCounter(OVERLAPPING_CONTINUE) + ">";
                            tagEnd = "</" + getNextOverlappingCounter(OVERLAPPING_CONTINUE) + ">";
                        }
                        else if (tag.Equals(TAG_OVERLAPPING_NEW))
                        {
                            tagStart = "<" + getNextOverlappingCounter(OVERLAPPING_NEXT) + ">";
                            tagEnd = "</" + getNextOverlappingCounter(OVERLAPPING_CONTINUE) + ">";
                        }
                        else if (tag.Equals(TAG_TIMESTAMP))
                        {
                            tagStart = getTimeCode();
                            tagEnd = "";
                        }
                        else if (tag.Equals(TAG_RETRO_RANGE))
                        {
                            tagStart = "<retro id=\"x\">";
                            tagEnd = "</retro>";
                        }
                        break;
                }

                // select everything from the index to the end
                // MessageBox.Show("SelectionStart: " + rtb.SelectionStart.ToString() + "\nSelectionLength: " + rtb.SelectionLength);
                rtb.Select(selStart, selLen);

                int blank = 0;
                if (rtb.SelectedText.Length > 0)
                {
                    if (rtb.SelectedText[rtb.SelectedText.Length - 1].ToString().Equals(" "))
                    {
                        rtb.Select(selStart, selLen - 1);
                        blank = 1;
                    }
                }

                // 1. Put tags around the selected text
                cache = tagStart + rtb.SelectedText + tagEnd; // add everything to what needs to be appended

                // 2. Grab the inserted strings length to re-position the cursor later
                int insertedStringLength = cache.Length;

                // 3. Tags + selected text + rest
                rtb.Select(selStart + selLen - blank, rtb.Text.Length);
                cache = cache + rtb.SelectedText;

                // 4. Select everything after the selection
                rtb.Select(selStart, rtb.Text.Length);

                // 5. Clear selection and everything afterwards
                rtb.SelectedText = String.Empty; // clear the selected text

                // 6. Append cache (tag + selection + tag + rest)
                rtb.AppendText(cache); // append the string

                resetCursor(CURSOR_POS_NEW, insertedStringLength);

                //return; // return from the function
            }
            catch (Exception ie)
            {
                alert("Error: " + ie.Message.ToString() + "\nCode: 101A");
            }
        }

        private int getLineFromCursorPos()
        {
            // stolen from here: http://stackoverflow.com/questions/2425847/current-line-and-column-numbers-in-a-richtextbox-in-a-winforms-application
            return getTextBox().GetLineFromCharIndex(getTextBox().SelectionStart);
        }

        private void insertStringAtLinePos(string text, int line, int pos)
        {
            string[] lines = getTextBox().Lines;
            try
            {
                string cache = "";
                resetCursor(CURSOR_POS_OLD, 0);
                string aLine = lines[line];
                for (int i = 0; i < aLine.Length - 1; i++)
                {
                    if (i != pos)
                    {
                        cache = cache + aLine[i];
                    }
                    else
                    {
                        cache = cache + text + aLine[i];
                    }
                }
                lines[line] = cache;
                getTextBox().Lines = lines;
                resetCursor(CURSOR_POS_NEW, text.Length);
            }
            catch (IndexOutOfRangeException ie)
            {
                alert("You have to select a transcript or create a new one first.\n\nError: "+ie.Message.ToString()+"\nCode: 102A");
            }
        }

        private void insertStringAtLineStart(string text, int line)
        {
            string[] lines = getTextBox().Lines;
            try
            {
                resetCursor(CURSOR_POS_OLD, 0);
                lines[line] = text + lines[line];
                getTextBox().Lines = lines;
                resetCursor(CURSOR_POS_NEW, text.Length);
            }
            catch (IndexOutOfRangeException ie)
            {
                if (lines.Count() == 0)
                {
                    MessageBox.Show("You have to select a transcript or create a new one first.", "Select transcript", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                }
                else
                {
                    alert("Error:" +ie.Message.ToString() + "\nCode: 104A");
                }
            }
        }

        private bool hasLineTimestamp(int line)
        {
            string[] lines = getTextBox().Lines;
            if (getTextBox().Lines.Count() > 0)
            {
                if (isValidTimestamp(getTextBox().Lines[line]))
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
            else
            {
                return false;
            }
        }

        private int getTimestampLength()
        {
            return getTextBox().Lines[getLineFromCursorPos()].IndexOf("]")+2; // +2 because want to jump straight to the whitespace after ']'
        }

        private int getAmountOfTimestamps()
        {
            int cache = 0; 
            if (getTextBox().Lines[getLineFromCursorPos()].IndexOf("]") > 0)
            {
                for (int i = 0; i < getTextBox().Lines[getLineFromCursorPos()].Length; i++)
                {
                    if (getTextBox().Lines[getLineFromCursorPos()][i].Equals("]"))
                    {
                        cache++;
                    }

                    if (i > 12)
                    {
                        break;
                    }

                }
            }
            return cache;
        }

        private void removeTimestamp(int timestamp)
        {
            if (timestamp == 0)
                // So we want to replace the first timestamp
            {
                bool removedTimestamp = false;
                for (int i = 1; i < getTextBox().Lines[getLineFromCursorPos()].Length; i++)
                {
                    // walk through the whole line
                    if (getTextBox().Lines[getLineFromCursorPos()][i].Equals("[") && (removedTimestamp == false))
                    {
                        // if the indexed character matches '[', we want to delete it
                        removedTimestamp = true;
                        
                        // TODO: How do I delete a single character? :-(
                        //getTextBox().Lines[getLineFromCursorPos()][i] = "";
                    }
                }
            }
            else if (timestamp == 1)
            {

            }
            else
            {
                alert("Programming error! Please contact achim.skuta@googlemail.com");
            }
        }

        private void insertTime()
        {
            // Das funktioniert nicht ... wirft einen Fehler, wenn es Zeilen gibt, die über eine Zeile hinausgehen
            //if (hasLineTimestamp(getLineFromCursorPos())) 
            //{
            //    //insertStringAtLinePos(getTimeCode(), getLineFromCursorPos(), getTimestampLength());
            //}
            //else 
            //{
            //    //insertStringAtLineStart(getTimeCode(), getLineFromCursorPos());
            //}

            // TODO: Insert Timestamp here
            AppendTextToIndex(TAG_TIMESTAMP, SPECIALTAG);
        }

        #endregion

        #region DEFINITION: VIDEO PLAYERS

        private string getTimeCode()
        {
            double offset = Decimal.ToDouble(numOffset.Value);
            double theTime = wmPlayer.Ctlcontrols.currentPosition + offset;

            string cache = theTime.ToString();
            string result = "";

            if (cache.IndexOf(",") > 0)
            {
                for (int i = 0; i <= cache.IndexOf(","); i++)
                {
                    result = result + cache.ElementAt(i);
                }
                result = result + cache.ElementAt(cache.IndexOf(",") + 1) + cache.ElementAt(cache.IndexOf(",") + 2);
            }
            else
            {
                result = "0";
            }

            result = "[" + result + "] ";

            return result;
        }

        private void player_ClickEvent(object sender, AxWMPLib._WMPOCXEvents_ClickEvent e)
        {
            playerClick();
        }

        private void loadFileToPlayer(int player, string aFile)
        {
            if (player == PLAYER_WMP)
            {
                wmPlayer.URL = @aFile;
            }
            else if (player == PLAYER_VLC)
            {
                //vlcPlayer.playlistClear();
                //vlcPlayer.Volume = 50;
                //vlcPlayer.addTarget(@aFile, null, AXVLC.VLCPlaylistMode.VLCPlayListAppendAndGo, -666);
            }
        }

        private void playerClick()
        {
            if (wmPlayer.playState.ToString().Equals("wmppsPlaying"))
            {
                wmPlayer.Ctlcontrols.pause();
            }
            else if (wmPlayer.playState.ToString().Equals("wmppsPaused") || wmPlayer.playState.ToString().Equals("wmppsReady"))
            {
                wmPlayer.Ctlcontrols.play();
            }
            else if (wmPlayer.playState.ToString().Equals("wmppsUndefined"))
            {
                btnLoadVideo_Click(null, null);
            }
        }

        #endregion

        private void insertTag(object sender)
        {
            string aCaption = "";
            if (sender.GetType().ToString().Equals("System.Windows.Forms.ComboBox"))
                aCaption = cmbxTag.Text;
            else if (sender.GetType().Equals("Button"))
                aCaption = (sender as System.Windows.Forms.Button).Name;
            else if (sender.GetType().ToString().Equals("System.Windows.Forms.ToolStripMenuItem"))
                aCaption = (sender as ToolStripMenuItem).Text;
            //alert(aCaption);

            if (aCaption.Equals(CAPTION_ALTERNATIVE))
                AppendTextToIndex(TAG_ALTERNATIVE, DOUBLETAG);
            else if (aCaption.Equals(CAPTION_BREAK))
                AppendTextToIndex(TAG_BREAK, SINGLETAG);
            else if (aCaption.Equals(CAPTION_COMMENTS))
                AppendTextToIndex(TAG_COMMENTS, SPECIALTAG);
            else if (aCaption.Equals(CAPTION_CUT))
                AppendTextToIndex(TAG_CUT, SINGLETAG);
            else if (aCaption.Equals(CAPTION_FOREIGN))
                AppendTextToIndex(TAG_FOREIGN, DOUBLETAG);
            else if (aCaption.Equals(CAPTION_OVERLAPPING_CONTINUED))
                AppendTextToIndex(TAG_OVERLAPPING_CONTINUED, SPECIALTAG);
            else if (aCaption.Equals(CAPTION_OVERLAPPING_NEW))
                AppendTextToIndex(TAG_OVERLAPPING_NEW, SPECIALTAG);
            else if (aCaption.Equals(CAPTION_QUOTES_DOUBLE))
                AppendTextToIndex(TAG_DOUBLEQUOTES, SPECIALTAG);
            else if (aCaption.Equals(CAPTION_QUOTES_SINGLE))
                AppendTextToIndex(TAG_SINGLEQUOTES, SPECIALTAG);
            else if (aCaption.Equals(CAPTION_SECTIONBOUNDARY))
                AppendTextToIndex(TAG_SECTIONBOUNDARY, SPECIALTAG);
            else if (aCaption.Equals(CAPTION_SECTIONCOMMENTS))
                AppendTextToIndex(TAG_SECTIONCOMMENT, SPECIALTAG);
            else if (aCaption.Equals(CAPTION_TRUNC))
                AppendTextToIndex(TAG_TRUNC, DOUBLETAG);
            else if (aCaption.Equals(CAPTION_UNCLEAR_DOUBLE))
                AppendTextToIndex(TAG_UNCLEAR, DOUBLETAG);
            else if (aCaption.Equals(CAPTION_UNCLEAR_SINGLE))
                AppendTextToIndex(TAG_UNCLEAR, SINGLETAG);

        }

        private void cmbxTag_TextChanged(object sender, EventArgs e)
        {
            insertTag(sender);
            
        }

        private void changeTagButtonState(int state)
        {
            bool show = false;

            if (state == TAGBUTTON_HIDE)
            {
                show = false;

                // changes in the top panel
                memText.Width = pnlTop.Size.Width - 12 - cmbxTag.Width;

                // changes in the lower panel
                numOffset.Width = cmbxTag.Width;
                btnTimeStamp.Width = cmbxTag.Width;
            }
            else if (state == TAGBUTTON_SHOW)
            {
                show = true;

                // changes in the top panel
                memText.Width = pnlTop.Size.Width - 8 - btnBreak.Width - btnBreak.Width - 8;    

                // changes in the lower panel
                numOffset.Width = btnBreak.Width;
                btnTimeStamp.Width = btnBreak.Width;
            }
            
            // player size
            wmPlayer.Width = pnlPlayer.Width - (3 + btnLoadVideo.Width + 6 + 6 + btnTimeStamp.Width + 3) -2;
            // button position in the lower panel
            numOffset.Left = wmPlayer.Left + wmPlayer.Width + 6;
            btnTimeStamp.Left = numOffset.Left;

            // Toggle Button Visibility
            btnAlternative.Visible = show;
            btnBreak.Visible = show;
            btnComments.Visible = show;
            btnCut.Visible = show;
            btnForeign.Visible = show;
            btnOverlappingContinued.Visible = show;
            btnOverlappingNew.Visible = show;
            btnQuotes.Visible = show;
            btnRetrospective.Visible = show;
            btnSectionBoundary.Visible = show;
            btnSectionComments.Visible = show;
            btnSingleQuotes.Visible = show;
            btnTrunc.Visible = show;
            btnUnclear1.Visible = show;
            btnUnclear2.Visible = show;
            btnRetroId.Visible = show;

            cmbxTag.Visible = !show;
            cmbxTag.Left = memText.Left + memText.Width + 4;
        }


        private void mnuTagButtons_Click(object sender, EventArgs e)
        {
            if (mnuTagButtons.Checked)
            {
                changeTagButtonState(TAGBUTTON_SHOW);
            }
            else if (!mnuTagButtons.Checked)
            { 
                changeTagButtonState(TAGBUTTON_HIDE); 
            }
        }

        private void contextMenuTagItem_Click(object sender, EventArgs e)
        {
            //alert((sender as ToolStripMenuItem).Text);
            insertTag(sender);
        }

        private void smallVideoPlayerToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (mnuVideoPlayerSize.Text.Equals("Switch to Big Player")) {
                // make it big!
                // optimal size for the wmplayer in minimalized shape: 296 px height

                frmTranscriptor.ActiveForm.Height = 590;

                wmPlayer.Top = 6;
                btnTimeStamp.Height = 23;

                pnlTop.Top = mnuBar.Height + 3;
                pnlTop.Height = 204; 
                
                pnlPlayer.Height = 311;
                pnlPlayer.Top = pnlTop.Top + pnlTop.Height + 6;

                mnuVideoPlayerSize.Text = "Switch to Small Player";
            } else if (mnuVideoPlayerSize.Text.Equals("Switch to Small Player")) {
                // make it small!
                // optimal size for the wmplayer in minimalized shape: 45 px height

                frmTranscriptor.ActiveForm.Height = 337;

                //wmPlayer.Height = 45;
                wmPlayer.Top = 6;
                btnTimeStamp.Height = 20;
                
                pnlTop.Top = mnuBar.Height + 3;
                pnlTop.Height = 204;

                pnlPlayer.Height = 60;
                pnlPlayer.Top = pnlTop.Top + pnlTop.Height + 6;

                mnuVideoPlayerSize.Text = "Switch to Big Player";
            }
        }

        private void memText_KeyUp(object sender, KeyEventArgs e)
        {
            // dirtiest workaround ever
            // prevent the text from being aligned to the right
             memText.SelectionAlignment = HorizontalAlignment.Left;
        }

        private void cmbxTag_MouseClick(object sender, MouseEventArgs e)
        {
            // dirty hack
            // remove the text of the combobox so the text will always be changed when the user
            // clicks on an item
            cmbxTag.Text = "";
        }

        private void button1_Click_1(object sender, EventArgs e)
        {
            AppendTextToIndex(TAG_RETRO_RANGE, SPECIALTAG);
        }
    }
}
